#include <pthread.h>
#include <iostream>
#include <queue>

using namespace std;

template <class T>
class BlockQueue
{
public:
    BlockQueue(int cap, pthread_mutex_t &mutex, pthread_cond_t &pro_cond, pthread_cond_t &con_cond)
        : _cap(cap), _mutex(mutex), _product_cond(pro_cond), _consumer_cond(con_cond)
    {
    }

    void Enqueue(const T &data, string name) //  生产者用的，放数据
    {
        pthread_mutex_lock(&_mutex); // 加锁
        cout << endl
             << "*************\n生产者>> 当前进入临界区的是" << name << endl;

        while (is_Full()) // 满了，就去【生产条件变量】下去等着
        {
            _product_wait_num++;
            cout << "生产者>> 满了," << name << "去等待,当前等待的生产者数是:" << _product_wait_num << endl
                 << "*************" << endl;
            pthread_cond_wait(&_product_cond, &_mutex);
            _product_wait_num--;
            cout << endl
                 << "***\n生产者>> 被唤醒重新进入临界区的是：" << name << endl;
        }
        cout << "生产者>> " << name << "生产了一个数据：" << data << endl;
        _block_queue.push(data); // 没满，或被唤醒了，就去放数据

        if (_consumer_wait_num > 0)
        {
            cout << "生产者>> 当前等待消费者数是" << _consumer_wait_num << "," << name << "唤醒一个消费者" << endl;
            pthread_cond_signal(&_consumer_cond); // 去唤醒等着的消费者，说有数据了，可以拿了
        }
        else
        {
            cout << "生产者>> 当前没有等待的消费者，" << name << "不唤醒消费者" << endl;
        }
        cout << "生产者>> " << name << "退出临界区 " << endl
             << "*************" << endl;
        pthread_mutex_unlock(&_mutex); // 解锁
    }
    //// 没有打印一些文字的
    // void Enqueue(const T &data, string name) //  生产者用的，放数据
    // {
    //     pthread_mutex_lock(&_mutex); // 加锁

    //     while (is_Full()) // 满了，就去【生产条件变量】下去等着
    //     {
    //         _product_wait_num++;
    //         pthread_cond_wait(&_product_cond, &_mutex);
    //         _product_wait_num--;
    //     }
    //     _block_queue.push(data); // 没满，或被唤醒了，就去放数据

    //     if (_consumer_wait_num > 0)
    //     {
    //         pthread_cond_signal(&_consumer_cond); // 去唤醒等着的消费者，说有数据了，可以拿了
    //     }

    //     pthread_mutex_unlock(&_mutex); // 解锁
    // }

    void Outqueue(T *data, string name) // 消费者用的，拿数据
    {
        pthread_mutex_lock(&_mutex); // 加锁
        cout << endl
             << "*************\n消费者 >>当前进入临界区的是 " << name << endl;

        while (is_Empty()) // 空了，就去【消费者条件变量】下去等着
        {
            _consumer_wait_num++;
            cout << "消费者>> 空了," << name << "去等待,当前等待的消费者数是：" << _consumer_wait_num << endl
                 << "*************" << endl;
            pthread_cond_wait(&_consumer_cond, &_mutex);
            _consumer_wait_num--;
            cout << endl
                 << "***\n消费者>> 被唤醒重新进入临界区的是：" << name << endl;
        }
        cout << "消费者>> " << name << "消费了一个数据：" << _block_queue.front() << endl;
        *data = _block_queue.front(); // 没空，或被唤醒了，就去拿数据
        _block_queue.pop();

        if (_product_wait_num > 0)
        {
            cout << "消费者>> 当前等待生产者数是" << _product_wait_num << "," << name << "唤醒一个生产者 " << endl;
            pthread_cond_signal(&_product_cond); // 去唤醒等着的生产者，说我拿数据了，可以继续生产了
        }
        else
        {
            cout << "消费者>> 当前没有等待的生产者，" << name << "不唤醒生产者" << endl;
        }
        cout << "消费者>> " << name << "退出临界区" << endl
             << "*************" << endl;
        pthread_mutex_unlock(&_mutex); // 解锁
    }

    //// 没有打印一些文字的
    //  void Outqueue(T *data, string name) // 消费者用的，拿数据
    //  {
    //      pthread_mutex_lock(&_mutex); // 加锁

    //     while (is_Empty()) // 空了，就去【消费者条件变量】下去等着
    //     {
    //         _consumer_wait_num++;
    //         pthread_cond_wait(&_consumer_cond, &_mutex);
    //         _consumer_wait_num--;
    //     }
    //     *data = _block_queue.front(); // 没空，或被唤醒了，就去拿数据
    //     _block_queue.pop();

    //     if (_product_wait_num > 0)
    //     {
    //         pthread_cond_signal(&_product_cond); // 去唤醒等着的生产者，说我拿数据了，可以继续生产了
    //     }

    //     pthread_mutex_unlock(&_mutex); // 解锁
    // }

    bool is_Full() const
    {
        return _block_queue.size() == _cap;
    }
    bool is_Empty() const
    {
        return _block_queue.empty();
    }

private:
    queue<T> _block_queue;
    int _cap;

    // 锁
    pthread_mutex_t &_mutex;

    // 条件变量
    pthread_cond_t &_product_cond;
    pthread_cond_t &_consumer_cond;

    // 在product/consume条件变量下，等待的线程的数目
    int _product_wait_num = 0;
    int _consumer_wait_num = 0;
};